/*
 * modbus.c
 *
 *  Created on: Jun 10, 2024
 *      Author: PIANO
 */

#include "modbus.h"
#include <string.h>

/**
 * 计算CRC16
 *
 * @param
 * @return
 */
static uint16_t getCrc16(uint8_t *buffer, uint16_t count) {
	uint16_t crc16 = 0xFFFF;
	uint8_t i, j, temporary;

	for (i = 0; i < count; i++) {
		crc16 ^= *buffer++;
		for (j = 0; j < 8; j++) {
			temporary = crc16 & 0x0001;
			crc16 = crc16 >> 1;
			if (temporary) {
				crc16 = crc16 ^ 0xA001;
			}
		}
	}
	return crc16;
}

/**
 * 验证CRC16
 *
 * @param
 * @return
 */
static bool verifyCrc16(uint8_t *rxBuffer, uint16_t count) {
	uint16_t crc16;

	crc16 = getCrc16((uint8_t*) rxBuffer, count - 2);
	if (((crc16 & 0x00FF) == rxBuffer[count - 2])
			&& ((crc16 >> 8) == rxBuffer[count - 1])) {
		return true;
	} else {
		return false;
	}
}

/**
 * 创建请求报文
 *
 * @param USARTx_Data
 * @return
 */
uint16_t modbusMasterBuild(uint16_t slaveAddress, uint16_t functionCode,
		uint16_t startAddress, uint16_t length, uint16_t *pData,
		uint8_t *txBuffer) {
	// 报文长度（BYTE）
	uint16_t count = 0;
	//从机地址
	txBuffer[0] = slaveAddress & 0xFF;
	//功能码
	txBuffer[1] = functionCode & 0xFF;
	//起始地址
	txBuffer[2] = startAddress >> 8;
	txBuffer[3] = startAddress & 0x00FF;
	//数据长度
	txBuffer[4] = length >> 8;
	txBuffer[5] = length & 0x00FF;
	//信息 + CRC
	switch (functionCode) {
	case READ_COIL_STATUS:
	case READ_DISCRETE_INPUT:
	case READ_HOLDING_REGISTER:
	case READ_INPUT_REGISTER:
		//CRC
		uint16_t crc = getCrc16((uint8_t*) txBuffer, 6);
		txBuffer[6] = crc & 0x00FF;
		txBuffer[7] = crc >> 8;
		count = 8;
		break;
	case WRITE_SINGLE_COIL:

		break;
	case WRITE_SINGLE_REGISTER:

		break;
	case WRITE_MULTIPLE_COIL:

		break;
	case WRITE_MULTIPLE_REGISTER:
		//字节数
		txBuffer[6] = length * 2;
		//数据
		for (int i = 0; i < length; i++) {

			txBuffer[7 + i * 2] = *(pData + i) >> 8;
			txBuffer[8 + i * 2] = *(pData + i) & 0x00FF;
		}
		//CRC
		crc = getCrc16((uint8_t*) txBuffer, length * 2 + 7);
		txBuffer[7 + length * 2] = crc & 0x00FF;
		txBuffer[8 + length * 2] = crc >> 8;

		count = 9 + length * 2;
		break;
	}
	// return
	return count;
}
/**
 * 解析响应报文
 *
 * @param USARTx_Data
 * @return
 */
uint16_t modbusMasterParse(uint8_t *txBuffer, uint8_t *rxBuffer,
		uint16_t *pData) {
	// 数据长度
	uint16_t result = 0;
	// 判断从机地址
	if (rxBuffer[0] != txBuffer[0]) {
		return 0;
	}
	//解析指令
	switch (txBuffer[1]) {
	//
	case READ_COIL_STATUS:

		break;
	case READ_DISCRETE_INPUT:

		break;
	case READ_HOLDING_REGISTER:
	case READ_INPUT_REGISTER:
		uint16_t count = (txBuffer[4] << 8) + txBuffer[5];
		if (verifyCrc16((uint8_t*) rxBuffer, count * 2 + 5)) {
			for (int i = 0; i < count; i++) {

				pData[i] = (rxBuffer[3 + i * 2] << 8) + rxBuffer[4 + i * 2];
			}
			result = count;
		}
		break;
	case WRITE_SINGLE_COIL:
	case WRITE_SINGLE_REGISTER:
	case WRITE_MULTIPLE_COIL:
	case WRITE_MULTIPLE_REGISTER:
		if (verifyCrc16((uint8_t*) rxBuffer, 8)) {
			uint16_t count = (txBuffer[4] << 8) + txBuffer[5];
			for (int i = 0; i < 6; i++) {
				if (rxBuffer[i] != txBuffer[i]) {
					count = 0;
					break;
				}
			}
			result = count;
		}
		break;
	default:

		break;
	}
	// return
	return result;
}

/**
 * MODBUS主站处理程序
 *
 * @param
 * @return
 */
void modbusMaster(UART_HandleTypeDef *huart, UART_DataTypeDef *duart,
		uint8_t slaveAddress, uint8_t functionCode, uint16_t startAddress,
		uint16_t length, uint16_t *pData) {
	// IDLE
	if (duart->status == UART_IDLE) {
		if (duart->interval > duart->intervalSet)
			duart->status = UART_START;
	}
	// START
	else if (duart->status == UART_START) {
		//清零重试次数
		duart->retryTimes = 0;
		duart->status = UART_BUILD;
	}
	// BUILD
	else if (duart->status == UART_BUILD) {
		//创建请求报文
		duart->txCount = modbusMasterBuild(slaveAddress, functionCode,
				startAddress, length, pData, duart->txBuffer);
		duart->status = UART_SEND;
	}
	//SEND
	else if (duart->status == UART_SEND) {
		memset(duart->rxBuffer, 0, sizeof(duart->rxBuffer));
		duart->rxCount = 0;
		duart->timeOut = 0;
		uartSend(huart, duart);
		duart->status = UART_RECEIVE;
	}
	//RECEIVE
	else if (duart->status == UART_RECEIVE) {
		if (duart->rxCount > 0 && duart->timeOut > 10) {
			//解析响应报文
			uint16_t count = modbusMasterParse(duart->txBuffer, duart->rxBuffer,
					pData);
			if (count != 0) {
				duart->status = UART_OK;
				duart->interval = 0;
			}
		}
		// 串口接收
		if (duart->status == UART_RECEIVE) {
			if (duart->timeOut > duart->timeOutSet) {
				duart->status = UART_RETRY;
			}
		}
	}
	//RETRY
	else if (duart->status == UART_RETRY) {

		if (duart->retryTimes < duart->retryTimesSet) {
			duart->retryTimes++;
			duart->status = UART_BUILD;
		} else {
			if (duart->rxCount > 0) {
				duart->status = UART_ACKERROR;
				duart->interval = 0;
			} else {
				duart->status = UART_TIMEOUT;
			}
		}
	}
	// DONE
	else if (duart->status >= UART_OK) {

	}
	//else
	else {
		duart->status = UART_ERROR;
		duart->interval = 0;
	}
}

/**
 * 创建MODBUS-RTU响应报文
 *
 * @param
 * @return
 */
void modbusBuildRespone(UART_DataTypeDef *duart,
		MODBUS_SlaveDataTypeDef *pSlaveData) {

	uint16_t startAddress, length, bytes, crc;
	uint16_t i, data;
	//从机地址
	duart->txBuffer[0] = pSlaveData->address;
	//功能码
	duart->txBuffer[1] = duart->rxBuffer[1];
	//判断功能码，创建报文数据
	switch (duart->rxBuffer[1]) {
	case READ_COIL_STATUS:

		break;
	case READ_DISCRETE_INPUT:
		// 起始地址和数据长度
		startAddress = (duart->rxBuffer[2] << 8) + duart->rxBuffer[3];
		length = (duart->rxBuffer[4] << 8) + duart->rxBuffer[5];
		//字节个数
		bytes = (length + 7) / 8;
		duart->txBuffer[2] = bytes;
		//数据
		memset(&duart->txBuffer[3], 0, 256 - 1 - 1 - 1 - 2);
		for (i = 0; i < length; i++) {

			uint16_t address = startAddress + i;
			if (address < pSlaveData->discreteInputCount) {
				data = pSlaveData->pDiscreteInput[startAddress + i];
			} else {
				data = 0;
			}
			// 数据
			if (data) {
				duart->txBuffer[3 + (i / 8)] |= (1 << (i % 8));
			}
		}
		//CRC
		crc = getCrc16((uint8_t*) &duart->txBuffer, 3 + bytes);
		duart->txBuffer[3 + bytes] = crc & 0x00FF;
		duart->txBuffer[4 + bytes] = crc >> 8;
		//发送字节数
		duart->txCount = 1 + 1 + 1 + bytes + 2;

		break;
	case READ_HOLDING_REGISTER:
		// 起始地址和数据长度
		startAddress = (duart->rxBuffer[2] << 8) + duart->rxBuffer[3];
		length = (duart->rxBuffer[4] << 8) + duart->rxBuffer[5];
		//字节个数
		duart->txBuffer[2] = length * 2;
		//数据
		for (i = 0; i < length; i++) {

			uint16_t address = startAddress + i;
			if (address < pSlaveData->holdingRegisterCount) {
				data = pSlaveData->pHoldingRegister[startAddress + i];
			} else {
				data = 0;
			}
			duart->txBuffer[i * 2 + 3] = data >> 8;
			duart->txBuffer[i * 2 + 4] = data & 0x00FF;
		}
		//CRC
		crc = getCrc16((uint8_t*) &duart->txBuffer, length * 2 + 3);
		duart->txBuffer[length * 2 + 3] = crc & 0x00FF;
		duart->txBuffer[length * 2 + 4] = crc >> 8;
		//发送字节数
		duart->txCount = 1 + 1 + 1 + length * 2 + 2;

		break;
	case READ_INPUT_REGISTER:
		// 起始地址和数据长度
		startAddress = (duart->rxBuffer[2] << 8) + duart->rxBuffer[3];
		length = (duart->rxBuffer[4] << 8) + duart->rxBuffer[5];
		//字节个数
		duart->txBuffer[2] = length * 2;
		//数据
		for (i = 0; i < length; i++) {

			uint16_t address = startAddress + i;
			if (address < pSlaveData->inputRegisterCount) {
				data = pSlaveData->pInputRegister[startAddress + i];
			} else {
				data = 0;
			}
			duart->txBuffer[i * 2 + 3] = data >> 8;
			duart->txBuffer[i * 2 + 4] = data & 0x00FF;
		}
		//CRC
		crc = getCrc16((uint8_t*) &duart->txBuffer, length * 2 + 3);
		duart->txBuffer[length * 2 + 3] = crc & 0x00FF;
		duart->txBuffer[length * 2 + 4] = crc >> 8;
		//发送字节数
		duart->txCount = 1 + 1 + 1 + length * 2 + 2;

		break;
	case WRITE_SINGLE_COIL:

		break;
	case WRITE_SINGLE_REGISTER:

		break;
	case WRITE_MULTIPLE_COIL:

		break;
	case WRITE_MULTIPLE_REGISTER:

		break;
	default:
		// 异常码
		duart->txBuffer[1] += 0x80;
		// 不支持此功能码
		duart->txBuffer[2] = 0x01;
		//CRC
		crc = getCrc16((uint8_t*) &duart->txBuffer, 3);
		duart->txBuffer[3] = crc & 0x00FF;
		duart->txBuffer[4] = crc >> 8;
		//发送字节数
		duart->txCount = 5;

		break;
	}
	// 清除发送计数
	duart->rxCount = 0;
}

/**
 * 解析MODBUS RTU 请求报文
 *
 * @param USARTx
 * @return
 */
void modbusRequestParse(UART_DataTypeDef *duart,
		MODBUS_SlaveDataTypeDef *pSlaveData) {
	uint16_t bytes;
	bool verifyOk = false;
	//判断是否为本机地址
	if (duart->rxBuffer[0] != pSlaveData->address)
		return;
	//验证报文长度和CRC-16
	switch (duart->rxBuffer[1]) {
	case READ_COIL_STATUS:
		verifyOk = verifyCrc16(duart->rxBuffer, 8);
		break;
	case READ_DISCRETE_INPUT:
		verifyOk = verifyCrc16(duart->rxBuffer, 8);
		break;
	case READ_HOLDING_REGISTER:
		verifyOk = verifyCrc16(duart->rxBuffer, 8);
		break;
	case READ_INPUT_REGISTER:
		verifyOk = verifyCrc16(duart->rxBuffer, 8);
		break;
	case WRITE_SINGLE_COIL:
		verifyOk = verifyCrc16(duart->rxBuffer, 8);
		break;
	case WRITE_SINGLE_REGISTER:
		verifyOk = verifyCrc16(duart->rxBuffer, 8);
		break;
	case WRITE_MULTIPLE_COIL:
		bytes = duart->rxBuffer[6];
		if (bytes < 124) {
			verifyOk = verifyCrc16(duart->rxBuffer, bytes + 9);
		}
		break;
	case WRITE_MULTIPLE_REGISTER:
		//取出字节数
		if (duart->rxCount < 11) {

			return;
		}
		bytes = duart->rxBuffer[6];
		//报文总长度为 1 + 1 + 2 + 2 + 1 + bytes + 2
		if (duart->rxCount < bytes + 9) {

			return;
		}
		if (bytes <= 256 - 9) {

			verifyOk = verifyCrc16(duart->rxBuffer, bytes + 9);
		} else {

			verifyOk = false;
		}
		break;
		//未知指令
	default:
		verifyOk = false;
		break;
	}
	if (verifyOk) {

		modbusBuildRespone(duart, pSlaveData);
	} else {

	}
}

/**
 * MODBUS从站处理程序
 *
 * @param
 * @return
 */
void modbusSlave(UART_HandleTypeDef *huart, UART_DataTypeDef *duart,
		MODBUS_SlaveDataTypeDef *pSlaveData) {
	//请求报文至少8字节
	if (duart->rxCount > 7) {

		modbusRequestParse(duart, pSlaveData);
	}
	//响应
	if ((duart->txCount > 0) && (duart->interval > duart->intervalSet)) {

		uartSend(huart, duart);
	}
}
